package sample.game.othello.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;

import sample.game.othello.gui.OthelloPanel.State;

/**
 * オセロの画面用クラス。
 *
 */
public class OthelloWindow extends JFrame implements MouseListener, ActionListener {

	/** X方向のマスの数 */
	public static final int X_SIZE = 8;

	/** Y方向のマスの数 */
	public static final int Y_SIZE = 8;
	
	/** オセロパネルのリスト */
	private List<OthelloPanel> panelList;
	
	/** 現在のプレイヤーの色 */
	private State nowPlaying;
	
	/** CPUが実行中か判定するフラグ */
	private boolean isAutoPlay;
	
	/**
	 * コンストラクタ
	 */
	public OthelloWindow() {
		// タイトル
		this.setTitle("Othello");
		// 画面の表示位置
		this.setLocation(50, 50);
		// 画面のサイズ
		this.setSize(new Dimension(500, 550));
		// 画面のレイアウト
		this.setLayout(new GridLayout(8, 8, 1, 1));
		// 画面の背景色
		this.setBackground(Color.WHITE);
		
		// メニュー設定
		JMenuBar menuBar = new JMenuBar();
		JMenu menu1 = new JMenu("Game");
		JMenuItem menuItem1 = new JMenuItem(MenuItem.START.name());
		JMenuItem menuItem2 = new JMenuItem(MenuItem.NEXT.name());
		JMenuItem menuItem3 = new JMenuItem(MenuItem.EXIT.name());
		
		menuItem1.addActionListener(this);
		menuItem2.addActionListener(this);
		menuItem3.addActionListener(this);
		
		menu1.add(menuItem1);
//		menu1.add(menuItem2);
		menu1.addSeparator();
		menu1.add(menuItem3);
		
		menuBar.add(menu1);
		
		// メニューバー設定
		setJMenuBar(menuBar);

		// パネルの初期化
		panelList = new LinkedList<OthelloPanel>();
		
		for (int i=0;i < X_SIZE * Y_SIZE;i++) {
			OthelloPanel panel = new OthelloPanel(i);
			panel.addMouseListener(this);
			panelList.add(panel);
			add(panel);
		}
		isAutoPlay = false;
	}
	
	/**
	 * ゲーム開始
	 */
	public void start() {
		clear();
		panelList.get(OthelloPanel.idx(3, 3)).state(State.WHITE);
		panelList.get(OthelloPanel.idx(4, 3)).state(State.BLACK);
		panelList.get(OthelloPanel.idx(3, 4)).state(State.BLACK);
		panelList.get(OthelloPanel.idx(4, 4)).state(State.WHITE);
		
		nowPlaying = State.WHITE;
	}
	
	/**
	 * 次のプレーヤーへ変える
	 */
	public void next() {
		// JOptionPane.showMessageDialog(rootPane, "next() called");
		if (nowPlaying == null) return;
		
		nowPlaying = (State.WHITE.equals(nowPlaying)) ? State.BLACK : State.WHITE;
		
		if (State.BLACK.equals(nowPlaying)) {
			autoPlayer();
		}
	}
	
	/**
	 * CPU処理
	 */
	private void autoPlayer() {
		isAutoPlay = true;
		ExecutorService service = Executors.newSingleThreadExecutor();
		service.execute(new AutoPlayer(this));
		isAutoPlay = false;
	}
	
	/**
	 * プログラム終了
	 */
	public void exit() {
		int result = JOptionPane.showConfirmDialog(rootPane, "Exit ?", "Confirm", JOptionPane.OK_CANCEL_OPTION);
		if (result != JOptionPane.OK_OPTION) {
			return;
		}
		System.exit(0);
	}
	
	/**
	 * 初期化
	 */
	public void clear() {
		for (OthelloPanel p : panelList) {
			p.state(State.NONE);
		}
	}
	
	/**
	 * マウスクリック時の動作
	 */
	public void mouseClicked(MouseEvent e) {
		if (nowPlaying == null || isAutoPlay) return;
		
		Object source = e.getSource();
		
		// オセロパネルのみ対象とする
		if (source instanceof OthelloPanel) {
			// JOptionPane.showMessageDialog(rootPane, "Clicked");
			OthelloPanel p = (OthelloPanel) source;
			// 何も置かれていないとこを対象とする
			if (!p.same(State.NONE)) return;

			// 変えるとこがなければ後続は何もしない
			if (check(p.x(), p.y(), true) == 0) return;
			
			p.state(nowPlaying);
			next();
			isOver();
		}
	}
	
	/**
	 * 終了判定処理
	 */
	private void isOver() {
		boolean flag = true;
		for (OthelloPanel p : panelList) {
			if (p.same(State.NONE)) {
				flag = false;
			}
		}
		
		if (flag) {
			JOptionPane.showMessageDialog(rootPane, String.format("Black : %02d White : %02d", blackCount(), whiteCount()), "Game Over", JOptionPane.INFORMATION_MESSAGE);
		}
	}
	
	/**
	 * 黒のカウントを取得する。
	 * 
	 * @return 黒のカウント
	 */
	private int blackCount() {
		int count = 0;
		for (OthelloPanel p : panelList) {
			if (p.same(State.BLACK)) {
				count++;
			}
		}
		return count;
	}
	
	/**
	 * 白のカウントを取得する。
	 * 
	 * @return 白のカウント
	 */
	private int whiteCount() {
		int count = 0;
		for (OthelloPanel p : panelList) {
			if (p.same(State.WHITE)) {
				count++;
			}
		}
		return count;
	}
	
	/**
	 * 裏返しにできるかチェックする
	 * 
	 * @param x チェックしたパネルのX座標
	 * @param y チェックしたパネルのY座標
	 * @param flag true:チェック＋裏返し処理/false:チェックのみ
	 * @return 裏返し対象の数
	 */
	public int check(int x, int y, boolean flag) {
		
		List<OthelloPanel> tmpList = new LinkedList<OthelloPanel>();
		List<OthelloPanel> changeList = new LinkedList<OthelloPanel>();
		
		boolean ok = false;
		int tmpX = x;
		int tmpY = y;
		while(--tmpX >= 0) {
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).blank()) {
				break;
			}
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).same(nowPlaying)) {
				ok = true;
				break;
			}
			
			tmpList.add(panelList.get(OthelloPanel.idx(tmpX, tmpY)));
		}
		if (ok) {
			changeList.addAll(tmpList);
		}
		
		ok = false;
		tmpList.clear();
		tmpX = x;
		tmpY = y;
		while(++tmpX < X_SIZE) {
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).blank()) {
				break;
			}
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).same(nowPlaying)) {
				ok = true;
				break;
			}
			tmpList.add(panelList.get(OthelloPanel.idx(tmpX, tmpY)));
		}
		if (ok) {
			changeList.addAll(tmpList);
		}

		ok = false;
		tmpList.clear();
		tmpX = x;
		tmpY = y;
		while(--tmpY >= 0) {
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).blank()) {
				break;
			}
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).same(nowPlaying)) {
				ok = true;
				break;
			}
			tmpList.add(panelList.get(OthelloPanel.idx(tmpX, tmpY)));
		}
		if (ok) {
			changeList.addAll(tmpList);
		}

		ok = false;
		tmpList.clear();
		tmpX = x;
		tmpY = y;
		while(++tmpY < Y_SIZE) {
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).blank()) {
				break;
			}
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).same(nowPlaying)) {
				ok = true;
				break;
			}
			tmpList.add(panelList.get(OthelloPanel.idx(tmpX, tmpY)));
		}
		if (ok) {
			changeList.addAll(tmpList);
		}

		ok = false;
		tmpList.clear();
		tmpX = x;
		tmpY = y;
		while(--tmpX >= 0 && --tmpY >= 0) {
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).blank()) {
				break;
			}
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).same(nowPlaying)) {
				ok = true;
				break;
			}
			tmpList.add(panelList.get(OthelloPanel.idx(tmpX, tmpY)));
		}
		if (ok) {
			changeList.addAll(tmpList);
		}

		ok = false;
		tmpList.clear();
		tmpX = x;
		tmpY = y;
		while(++tmpX < X_SIZE && --tmpY >= 0) {
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).blank()) {
				break;
			}
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).same(nowPlaying)) {
				ok = true;
				break;
			}
			tmpList.add(panelList.get(OthelloPanel.idx(tmpX, tmpY)));
		}
		if (ok) {
			changeList.addAll(tmpList);
		}

		ok = false;
		tmpList.clear();
		tmpX = x;
		tmpY = y;
		while(--tmpX >= 0 && ++tmpY < Y_SIZE) {
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).blank()) {
				break;
			}
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).same(nowPlaying)) {
				ok = true;
				break;
			}
			tmpList.add(panelList.get(OthelloPanel.idx(tmpX, tmpY)));
		}
		if (ok) {
			changeList.addAll(tmpList);
		}

		ok = false;
		tmpList.clear();
		tmpX = x;
		tmpY = y;
		while(++tmpX < X_SIZE && ++tmpY < Y_SIZE) {
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).blank()) {
				break;
			}
			if (panelList.get(OthelloPanel.idx(tmpX, tmpY)).same(nowPlaying)) {
				ok = true;
				break;
			}
			tmpList.add(panelList.get(OthelloPanel.idx(tmpX, tmpY)));
		}
		if (ok) {
			changeList.addAll(tmpList);
		}

		if (flag) {
			for (OthelloPanel p : changeList) {
				p.state(nowPlaying);
			}
		}
		
		return changeList.size();
	}
	
	/**
	 * メニューボタンの処理
	 */
	public void actionPerformed(ActionEvent e) {
		Object source = e.getSource();
		
		if (!(source instanceof JMenuItem)) {
			return;
		}
		
		JMenuItem item = (JMenuItem) source;
		MenuItem.convert(item.getText()).execute(this);
	}
	
	public void mousePressed(MouseEvent e) {}
	public void mouseReleased(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}

	/**
	 * メニューアイテム用enum。
	 */
	private static enum MenuItem {
		START,
		NEXT,
		EXIT;
		
		public void execute(Object obj) {
			try {
				Method m = obj.getClass().getDeclaredMethod(name().toLowerCase());
				m.invoke(obj);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		
		public static MenuItem convert(String value) {
			for (MenuItem item : values()) {
				if (item.name().equals(value)) {
					return item;
				}
			}
			
			throw new IllegalArgumentException(value);
		}
	}
	
	/**
	 * CPU要クラス。
	 * とりあえず、一番裏返る数が多いところを選択する。
	 * 
	 */
	private static class AutoPlayer implements Runnable {
		
		private OthelloWindow window;
		
		public AutoPlayer(OthelloWindow window) {
			this.window = window;
		}
		
		public void run() {
			System.out.println("autoPlayer() start");
			try {
				TimeUnit.SECONDS.sleep(3);
			} catch (Exception e){}
			
			int idx = -1;
			
			int tmpC = 0;
			for (OthelloPanel p : window.panelList) {
				if (!p.same(State.NONE)) continue;
				
				int c = window.check(p.x(), p.y(), false);
				
				if (c != 0 && c > tmpC) {
					tmpC = c;
					idx = p.idx();
				}
			}
			
			if (idx != -1) {
				OthelloPanel p = window.panelList.get(idx);
				window.check(p.x(), p.y(), true);
				p.state(window.nowPlaying);
			}
			JOptionPane.showMessageDialog(window, "your turn.");
			window.next();
			window.isOver();
		}
	}
	/**
	 * バージョンID。
	 */
	private static final long serialVersionUID = 7552630852609754739L;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new OthelloWindow().setVisible(true);
	}
}
